/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2014, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* --------------------
* AttrStringUtils.java
* --------------------
* (C) Copyright 2013, 2014, by Object Refinery Limited and Contributors.
*
* Original Author: David Gilbert (for Object Refinery Limited);
* Contributor(s): -;
*
* Changes:
* --------
* 01-Aug-2013 : Version 1, backported from JFreeChart-FSE (DG);
* 18-Mar-2014 : Added getTextBounds() method (DG);
*
*/
package org.jfree.chart.util;
import java.awt.Graphics2D;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedString;
import org.jfree.ui.TextAnchor;
/**
* Some <code>AttributedString</code> utilities.
*
* @since 1.0.16
*/
public class AttrStringUtils {
private AttrStringUtils() {
// no need to instantiate this class
}
/**
* Returns the bounds for the attributed string.
*
* @param text the attributed string (<code>null</code> not permitted).
* @param g2 the graphics target (<code>null</code> not permitted).
*
* @return The bounds (never <code>null</code>).
*
* @since 1.0.18
*/
public static Rectangle2D getTextBounds(AttributedString text,
Graphics2D g2) {
TextLayout tl = new TextLayout(text.getIterator(),
g2.getFontRenderContext());
return tl.getBounds();
}
/**
* Draws the attributed string at <code>(x, y)</code>, rotated by the
* specified angle about <code>(x, y)</code>.
*
* @param text the attributed string (<code>null</code> not permitted).
* @param g2 the graphics output target.
* @param angle the angle.
* @param x the x-coordinate.
* @param y the y-coordinate.
*
* @since 1.0.16
*/
public static void drawRotatedString(AttributedString text, Graphics2D g2,
double angle, float x, float y) {
drawRotatedString(text, g2, x, y, angle, x, y);
}
/**
* Draws the attributed string at <code>(textX, textY)</code>, rotated by
* the specified angle about <code>(rotateX, rotateY)</code>.
*
* @param text the attributed string (<code>null</code> not permitted).
* @param g2 the graphics output target.
* @param textX the x-coordinate for the text.
* @param textY the y-coordinate for the text.
* @param angle the rotation angle (in radians).
* @param rotateX the x-coordinate for the rotation point.
* @param rotateY the y-coordinate for the rotation point.
*
* @since 1.0.16
*/
public static void drawRotatedString(AttributedString text, Graphics2D g2,
float textX, float textY, double angle, float rotateX,
float rotateY) {
ParamChecks.nullNotPermitted(text, "text");
AffineTransform saved = g2.getTransform();
AffineTransform rotate = AffineTransform.getRotateInstance(angle,
rotateX, rotateY);
g2.transform(rotate);
TextLayout tl = new TextLayout(text.getIterator(),
g2.getFontRenderContext());
tl.draw(g2, textX, textY);
g2.setTransform(saved);
}
/**
* Draws the string anchored to <code>(x, y)</code>, rotated by the
* specified angle about <code>(rotationX, rotationY)</code>.
*
* @param text the text (<code>null</code> not permitted).
* @param g2 the graphics target.
* @param x the x-coordinate for the text location.
* @param y the y-coordinate for the text location.
* @param textAnchor the text anchor point.
* @param angle the rotation (in radians).
* @param rotationX the x-coordinate for the rotation point.
* @param rotationY the y-coordinate for the rotation point.
*
* @since 1.0.16
*/
public static void drawRotatedString(AttributedString text, Graphics2D g2,
float x, float y, TextAnchor textAnchor,
double angle, float rotationX, float rotationY) {
ParamChecks.nullNotPermitted(text, "text");
float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor,
null);
drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle,
rotationX, rotationY);
}
/**
* Draws a rotated string.
*
* @param text the text to draw.
* @param g2 the graphics target.
* @param x the x-coordinate for the text location.
* @param y the y-coordinate for the text location.
* @param textAnchor the text anchor point.
* @param angle the rotation (in radians).
* @param rotationAnchor the rotation anchor point.
*
* @since 1.0.16
*/
public static void drawRotatedString(AttributedString text, Graphics2D g2,
float x, float y, TextAnchor textAnchor,
double angle, TextAnchor rotationAnchor) {
ParamChecks.nullNotPermitted(text, "text");
float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor,
null);
float[] rotateAdj = deriveRotationAnchorOffsets(g2, text,
rotationAnchor);
drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1],
angle, x + textAdj[0] + rotateAdj[0],
y + textAdj[1] + rotateAdj[1]);
}
private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2,
AttributedString text, TextAnchor anchor, Rectangle2D textBounds) {
TextLayout layout = new TextLayout(text.getIterator(), g2.getFontRenderContext());
Rectangle2D bounds = layout.getBounds();
float[] result = new float[3];
float ascent = layout.getAscent();
result[2] = -ascent;
float halfAscent = ascent / 2.0f;
float descent = layout.getDescent();
float leading = layout.getLeading();
float xAdj = 0.0f;
float yAdj = 0.0f;
if (isHorizontalCenter(anchor)) {
xAdj = (float) -bounds.getWidth() / 2.0f;
}
else if (isHorizontalRight(anchor)) {
xAdj = (float) -bounds.getWidth();
}
if (isTop(anchor)) {
//yAdj = -descent - leading + (float) bounds.getHeight();
yAdj = (float) bounds.getHeight();
}
else if (isHalfAscent(anchor)) {
yAdj = halfAscent;
}
else if (isHalfHeight(anchor)) {
yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0);
}
else if (isBaseline(anchor)) {
yAdj = 0.0f;
}
else if (isBottom(anchor)) {
yAdj = -descent - leading;
}
if (textBounds != null) {
textBounds.setRect(bounds);
}
result[0] = xAdj;
result[1] = yAdj;
return result;
}
/**
* A utility method that calculates the rotation anchor offsets for a
* string. These offsets are relative to the text starting coordinate
* (BASELINE_LEFT).
*
* @param g2 the graphics device.
* @param text the text.
* @param anchor the anchor point.
*
* @return The offsets.
*/
private static float[] deriveRotationAnchorOffsets(Graphics2D g2,
AttributedString text, TextAnchor anchor) {
float[] result = new float[2];
TextLayout layout = new TextLayout(text.getIterator(),
g2.getFontRenderContext());
Rectangle2D bounds = layout.getBounds();
float ascent = layout.getAscent();
float halfAscent = ascent / 2.0f;
float descent = layout.getDescent();
float leading = layout.getLeading();
float xAdj = 0.0f;
float yAdj = 0.0f;
if (isHorizontalLeft(anchor)) {
xAdj = 0.0f;
}
else if (isHorizontalCenter(anchor)) {
xAdj = (float) bounds.getWidth() / 2.0f;
}
else if (isHorizontalRight(anchor)) {
xAdj = (float) bounds.getWidth();
}
if (isTop(anchor)) {
yAdj = descent + leading - (float) bounds.getHeight();
}
else if (isHalfHeight(anchor)) {
yAdj = descent + leading - (float) (bounds.getHeight() / 2.0);
}
else if (isHalfAscent(anchor)) {
yAdj = -halfAscent;
}
else if (isBaseline(anchor)) {
yAdj = 0.0f;
}
else if (isBottom(anchor)) {
yAdj = descent + leading;
}
result[0] = xAdj;
result[1] = yAdj;
return result;
}
private static boolean isTop(TextAnchor anchor) {
return anchor.equals(TextAnchor.TOP_LEFT)
|| anchor.equals(TextAnchor.TOP_CENTER)
|| anchor.equals(TextAnchor.TOP_RIGHT);
}
private static boolean isBaseline(TextAnchor anchor) {
return anchor.equals(TextAnchor.BASELINE_LEFT)
|| anchor.equals(TextAnchor.BASELINE_CENTER)
|| anchor.equals(TextAnchor.BASELINE_RIGHT);
}
private static boolean isHalfAscent(TextAnchor anchor) {
return anchor.equals(TextAnchor.HALF_ASCENT_LEFT)
|| anchor.equals(TextAnchor.HALF_ASCENT_CENTER)
|| anchor.equals(TextAnchor.HALF_ASCENT_RIGHT);
}
private static boolean isHalfHeight(TextAnchor anchor) {
return anchor.equals(TextAnchor.CENTER_LEFT)
|| anchor.equals(TextAnchor.CENTER)
|| anchor.equals(TextAnchor.CENTER_RIGHT);
}
private static boolean isBottom(TextAnchor anchor) {
return anchor.equals(TextAnchor.BOTTOM_LEFT)
|| anchor.equals(TextAnchor.BOTTOM_CENTER)
|| anchor.equals(TextAnchor.BOTTOM_RIGHT);
}
private static boolean isHorizontalLeft(TextAnchor anchor) {
return anchor.equals(TextAnchor.TOP_LEFT)
|| anchor.equals(TextAnchor.CENTER_LEFT)
|| anchor.equals(TextAnchor.HALF_ASCENT_LEFT)
|| anchor.equals(TextAnchor.BASELINE_LEFT)
|| anchor.equals(TextAnchor.BOTTOM_LEFT);
}
private static boolean isHorizontalCenter(TextAnchor anchor) {
return anchor.equals(TextAnchor.TOP_CENTER)
|| anchor.equals(TextAnchor.CENTER)
|| anchor.equals(TextAnchor.HALF_ASCENT_CENTER)
|| anchor.equals(TextAnchor.BASELINE_CENTER)
|| anchor.equals(TextAnchor.BOTTOM_CENTER);
}
private static boolean isHorizontalRight(TextAnchor anchor) {
return anchor.equals(TextAnchor.TOP_RIGHT)
|| anchor.equals(TextAnchor.CENTER_RIGHT)
|| anchor.equals(TextAnchor.HALF_ASCENT_RIGHT)
|| anchor.equals(TextAnchor.BASELINE_RIGHT)
|| anchor.equals(TextAnchor.BOTTOM_RIGHT);
}
}